﻿using System;
using System.Collections.Generic;
using Core.ChannelProtocol;
using Core.DeviceProtocol;
using Core.Modbus;
using Core.Model;
using Core.Msg;

namespace IEC104Client
{
    public class IEC104Client : BaseDevProtocol
    {
        #region 属性字段
        // 发送队列
        private  readonly Queue<APDUClass> _sendList = new Queue<APDUClass>();
        private DateTime _recTime = DateTime.Now,_resAllTime = DateTime.Now.AddMinutes(-10); // 接收时间，总召唤时间
        private DateTime _recIFormate = DateTime.Now; // 接收I帧时间
        private bool _isRecIFormate;
        private int _IFormatAck = 8,_IRecCount,_time2 = 5/*不大于10*/, _timer3 = 10/*不大于15*/; // 计时器s 

        private int _resAllInterval = 15; // 总召唤时间间隔
        //发计数。程序中使用，实际应用中暂未起作用
        private short sr;
        //收计数。程序中使用，实际应用中暂未起作用
        private short nr;
        //建立数据索引，查找已初始化的数据更新
       private readonly Dictionary<int, DataItem> _dataYcMaps = new Dictionary<int, DataItem>();
       private readonly Dictionary<int, DataItem> _dataYxMaps = new Dictionary<int, DataItem>();
        #endregion

        #region 初始化协议信息
        public override bool Initialize(BaseChannel channel, Device dev, List<Operation> operations, List<DataItem> dataItems)
        {
            base.Initialize(channel, dev, operations, dataItems);
            return GetSendInfo();
        }
        /// <summary>
        /// 根据数据项信息获取发送报文信息 功能码 寄存器起始地址 个数 
        /// </summary>
        /// <returns></returns>
        protected virtual bool GetSendInfo()
        {
            if (Dev.GetBaseDevProtocol() == null || Dev.GetBaseDevProtocol().DataItems.Count == 0)
            {
                return false;
            }

            List<DataItem> dataItems = Dev.GetBaseDevProtocol().DataItems;
            foreach (DataItem item in dataItems)
            {
                if (item.PhyType == PhyType.模拟量)
                    _dataYcMaps[item.Address] = item;
                else
                    _dataYxMaps[item.Address] = item;
            }
            _sendList.Enqueue(new APDUClass(new APCIClassUFormat(APCIClassUFormat.UFormatType.StartSet), null)); // 启动帧
            sr = 0;
            nr = 0;
            /*            ASDUClass clockBuffer = new ASDUClass();
                        clockBuffer.Pack(ASDUClass.TransRes.Active, ASDUClass.FunType.ClockConfirm, CurDevice.DeviceAddr); 
                        _sendList.Enqueue(new APDUClass(new APCIClassIFormat(++sr, nr), clockBuffer));// 对时帧
                        ASDUClass calBuffer = new ASDUClass();
                        calBuffer.Pack(ASDUClass.TransRes.Active, ASDUClass.FunType.CalAll, CurDevice.DeviceAddr);
                        _sendList.Enqueue(new APDUClass(new APCIClassIFormat(++sr, nr), calBuffer));// 总召帧*/
            return true;
        }
        #endregion

        #region 轮询函数
        /// <summary>
        /// 轮询函数
        /// </summary>
        /// <param name="bRepeat">是否重发上一条报文</param>
        /// <returns></returns>
        public  bool RunPolling(bool bRepeat = false)
        {
            if (_isRecIFormate && (_IRecCount >= _IFormatAck || (DateTime.Now - _recIFormate).TotalSeconds > _time2))  // 应答I帧
            {
                _sendList.Enqueue(new APDUClass(new APCIClassSFormat(nr), null));
                _isRecIFormate = false;
                _recTime = DateTime.Now; // 重置测试U帧计时
            }
            else if ((DateTime.Now - _resAllTime).TotalMinutes >= _resAllInterval)
            {
                _resAllTime = DateTime.Now;
                ASDUClass calBuffer = new ASDUClass();
                calBuffer.Pack(ASDUClass.TransRes.Active, ASDUClass.FunType.CalAll, (int)Dev.Address);
                _sendList.Enqueue(new APDUClass(new APCIClassIFormat(sr++, nr), calBuffer));// 总召帧
            }
            else if ((DateTime.Now - _recTime).TotalSeconds > _timer3)
            {
                _sendList.Enqueue(new APDUClass(new APCIClassUFormat(APCIClassUFormat.UFormatType.TestSet), null));
                _recTime = DateTime.Now;
            }
            if (_sendList.Count>0)
            {
                APDUClass apdu = _sendList.Dequeue();
                
                RxTxCommand cmd = new RxTxCommand(Dev.ChannelId,Dev.Id, apdu.GetApciType() +"帧");
                cmd.SendBytes = apdu.ToArray();
                cmd.DealReceiveMsg = DealReceiveMsg;
                //cmd.NeedReply = false;
                this.Channel.PutCommand(cmd);
                return true;
            }
            else
            {

                return false;
            }
        }
        #endregion

        #region 接收数据处理
        private void DealReceiveMsg(object sender, EventArgs e)
        {
            ModbusRxTxCommand rx = sender as ModbusRxTxCommand;
            if (rx == null || rx.ReceiveResult != MsgResult.RecOk) return;
            ExplainRxData(rx.ReceiveBytes);
        }

        public  bool ExplainRxData(byte[] rxData)
        {
            if (rxData == null)
            {
                return false;
            }
            try
            {
                APDUClass temp = new APDUClass(rxData);
                
                APCIClass.UISFormat dataFormat = temp.GetApciType();
                _recTime = DateTime.Now; // t3 计时器
                if (dataFormat == APCIClass.UISFormat.I)
                {
                    if (temp.GetAsduClass() != null && temp.GetAsduClass().ApduAddr != (int)Dev.Address) // 与设备地址不对应
                    {
                        return false;
                    }
                    nr = (short)(temp.GetSR() + 1);
                    if (nr >= short.MaxValue)
                    {
                        nr = 0;
                    }
                    _recIFormate = DateTime.Now; // I帧接收时间
                    if (temp.Res == ASDUClass.TransRes.Init) // 响应总召唤
                    {
                        _isRecIFormate = false; // 是否启动I帧应答处理
                        _IRecCount = 0;         // 启动I帧应答处理时接收I帧计数
                        _resAllTime = DateTime.Now;
                        ASDUClass calBuffer = new ASDUClass();
                        calBuffer.Pack(ASDUClass.TransRes.Active, ASDUClass.FunType.CalAll, (int)Dev.Address);
                        _sendList.Enqueue(new APDUClass(new APCIClassIFormat(sr, nr), calBuffer));// 总召帧
                        sr++;
                        if (sr >= short.MaxValue)
                        {
                            sr = 0;
                        }
                    }
                    else if (temp.Res == ASDUClass.TransRes.ActiveEnd) // 总召唤结束
                    {
                        _isRecIFormate = false; // 是否启动I帧应答处理
                        _IRecCount = 0;         // 启动I帧应答处理时接收I帧计数
                        ASDUClass clockBuffer = new ASDUClass();
                        clockBuffer.Pack(ASDUClass.TransRes.Active, ASDUClass.FunType.ClockConfirm, (int)Dev.Address);
                        _sendList.Enqueue(new APDUClass(new APCIClassIFormat(sr, nr), clockBuffer));// 对时帧
                        sr++;
                        if (sr >= short.MaxValue)
                        {
                            sr = 0;
                        }
                    }
                    else
                    {
                        _isRecIFormate = true;  // 是否启动I帧应答处理
                        _IRecCount++;           // 启动I帧应答处理时接收I帧计数
                        //_sendList.Enqueue(new APDUClass(new APCIClassSFormat(nr), null));
                    }
                }
                else if (dataFormat == APCIClass.UISFormat.S)
                {
                    nr = (short)(temp.GetSR() + 1);
                    if (nr >= short.MaxValue)
                    {
                        nr = 0;
                    }
                    _sendList.Enqueue(new APDUClass(new APCIClassSFormat(nr), null));
                }
                else if(dataFormat == APCIClass.UISFormat.U)
                {
                    APCIClassUFormat uapci = (APCIClassUFormat)temp.GetApciClass();
                    
                    if (uapci.UType == APCIClassUFormat.UFormatType.StartConfirm)
                    {
                        //_sendList.Enqueue(new APDUClass(new APCIClassUFormat(APCIClassUFormat.UFormatType.StartSet), null)); // 启动帧
                    }
                    else if (uapci.UType == APCIClassUFormat.UFormatType.TestSet)
                    {
                        _sendList.Enqueue(new APDUClass(new APCIClassUFormat(APCIClassUFormat.UFormatType.TestConfirm), null)); // 测试U帧
                    }
                }
                if (temp.Res == ASDUClass.TransRes.ResAll || temp.Res == ASDUClass.TransRes.AutoSend)
                {
                    var datas = temp.GetData();
                    var typeFlag = temp.GetAsduClass().GetTypeFlag();
                    Dictionary<int, DataItem> tempPhysicDataItems = null;
                    if (typeFlag == ASDUClass.FunType.Single_point || typeFlag == ASDUClass.FunType.Time_Single_point ||
                        typeFlag == ASDUClass.FunType.Double_point || typeFlag == ASDUClass.FunType.Time_Double_point)
                    {
                        tempPhysicDataItems = _dataYxMaps;
                    }
                    else if (typeFlag == ASDUClass.FunType.FloatMValue ||
                             typeFlag == ASDUClass.FunType.Time_FloatMValue||
                             typeFlag == ASDUClass.FunType.BackMValue ||
                             typeFlag == ASDUClass.FunType.Time_BackMValue)
                    {
                        tempPhysicDataItems = _dataYcMaps;
                    }
                    if (tempPhysicDataItems != null)
                    {
                        foreach (var data in datas)
                        {
                            DataItem dataItem;
                            if (tempPhysicDataItems.TryGetValue(data.Addr, out dataItem))
                            {
                                if (data.Data != null)
                                {
                                    dataItem.CurValue = (double)data.Data;
                                }
                                if (data.Time != null)
                                {
                                    dataItem.UpdateTime = (DateTime)data.Time;
                                }
                            }
                        }
                    }
                   
                }
                
            }
            catch 
            {
            }
            return false;
        }

        #endregion

        public override RxTxCommand WriteValue(string dataId, double value)
        {
            return null;
        }

        public override RxTxCommand WriteValue(string startId, byte[] buffer)
        {
            throw new NotImplementedException();
        }
    }
}
